/*
 * rowgage.c
 */

#include <stdlib.h>

#include <controller/controller.h>
#include <controller/listener.h>
#include <controller/timer.h>
#include <gui/gui_lua.h>
#include <lua/luaR.h>
#include <gamelogic/npc_lua.h>
#include <gameview/graphics.h>
#include <map/map_creator.h>
#include <map/map_handler.h>
#include <map/map_lua.h>
#include "rowgage.h"

#ifdef __APPLE_CC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#ifdef RG_VERBOSE
#define initprint(x) fprintf(stdout,x)
#else
#define initprint(x)
#endif

#define TURNARRAYSIZE 10

static int rowgage_init = 0;

/* Modules that can be enabled */
#define RG_NONE 0x00
#define RG_LUA 0x01
#define RG_CONT 0x02
#define RG_TCONT 0x04
#define RG_GUI 0x08
#define RG_GVIEW 0x10
#define RG_MAP 0x020
#define RG_NPC 0x040
#define RG_ALL 0xff

static unsigned char modules = RG_ALL;
static void (*turnarray[TURNARRAYSIZE+1])(void);
static void (*backarray[TURNARRAYSIZE+1])(void);
static int turnarray_size = 0;
static int backarray_size = 0;
static int swap_arrays = 0;

/*
 * Initialize the ROWGAGE framework, initializing all enabled modules.
 */
int rgInit( int (*logic_init)(void), int *argc, char **argv ) {
    int status = 0;
    if( rowgage_init ) {
        /* TODO: warning */
        return 1;
    }
    else {
        turnarray[0] = NULL;
        /* TODO: better error reporting regarding module dependencies */
        if( modules & RG_LUA && status == 0 ) {
            initprint( "Initializing Lua State... " );
            status |= luaR_initialize();
            initprint( "success!\n" );
        }
        if( modules & RG_CONT && status == 0 ) {
            initprint( "Initializing Controller... " );
            status |= controller_init();
            status |= listener_init();
            rgAddTurnCallback(controller_empty);
            initprint( "success!\n" );
        }
        if( modules & RG_TCONT && status == 0 ) {
            initprint( "Initializing Timed Controller... " );
            status |= timed_controller_create();
            initprint( "success!\n" );
        }
        if( modules & RG_MAP && status == 0 ) {
            initprint( "Initializing Game World... " );
            if( modules & RG_LUA ) {
                status |= map_lua_initialize();
                if( status == 0 ) {
                    status |= open_map("maps/random.rgm"); /* TODO: Load appropriate map */
                }
                initprint( "success!\n" );
            }
            else {
                initprint( "failed! Dependent module RG_LUA not initialized!\n" );
                status = 1;
            }
        }
        if( modules & RG_NPC && status == 0 ) {
            initprint( "Initializing Game Agents... " );
            if( modules & RG_LUA ) {
                status |= npc_lua_initialize();
                initprint( "success!\n" );
            }
            else {
                initprint( "failed! Dependent module RG_LUA not initialized!\n" );
                status = 1;
            }
        }
        if( modules & RG_GVIEW && status == 0 ) {
            initprint( "Initializing Game View... " );
            if( modules & RG_LUA ) {
                status |= graphics_init(argc, argv);
                initprint( "success!\n" );
            }
            else {
                initprint( "failed! Dependent module RG_LUA not initialized!\n" );
                status = 1;
            }
        }
        if( modules & RG_GUI && status == 0 ) {
            initprint( "Initializing GUI... " );
            if( modules & RG_LUA && modules & RG_GVIEW ) {
                status |= lua_gui_initialize();
                initprint( "success!\n" );
            }
            else {
                initprint( "failed! Dependent modules RG_LUA and RG_GVIEW not initialized!\n" );
                status = 1;
            }
        }
        status |= logic_init();
        if( status == 0 ) {
            rowgage_init = 1;
            initprint( "Initialization completed! Starting game...\n" );
            glutMainLoop();
        }
        else {
            fprintf( stderr, "Initialization failed!\n" );
        }
    }
    return 0;
}

/*
 * Enables ROWGAGE modules according to the given bit mask.
 */
void rgEnable( int mask ) {
    if( rowgage_init ) {
        /* TODO: warning */
    }
    else {
        modules |= mask;
    }
}

/*
 * Disables ROWGAGE modules according to the given bit mask.
 */
void rgDisable( int mask ) {
    if( rowgage_init ) {
        /* TODO: warning */
    }
    else {
        modules &= !mask;
    }
}

/*
 * Adds the given function to the ROWGAGE turn callback list.
 * Every game turn all functions in the turn callback list will be called. The
 * order is not necessarily the order in which they were added.
 */
int rgAddTurnCallback( void (*function)(void) ) {
    if( turnarray_size < TURNARRAYSIZE ) {
        turnarray[turnarray_size] = function;
        turnarray_size++;
        turnarray[turnarray_size] = NULL;
        return 0;
    }
    else {
        /* TODO: error */
        return 1;
    }
}

/*
 * Adds the given function to the ROWGAGE back callback list.
 * Every game turn when the turn arrays are swapped using rgCallbackSwap() all
 * functions in the back turn callback list will be called. The order is not
 * necessarily the order in which they were added.
 */
int rgAddBackCallback( void (*function)(void) ) {
    if( backarray_size < TURNARRAYSIZE ) {
        backarray[backarray_size] = function;
        backarray_size++;
        backarray[backarray_size] = NULL;
        return 0;
    }
    else {
        /* TODO: error */
        return 1;
    }
}

/*
 * Resets the back turn callback list. It is recommended that this function is
 * called whenever the callback lists are about to be swapped to ensure only the
 * correct callbacks are in the list.
 */
int rgBackListReset() {
    /* TODO: warning */
    return 1;
}

/*
 * Swaps the callback lists.
 */
int rgCallbackSwap() {
    swap_arrays = !swap_arrays;
    return 0;
}

/*
 * Sets the ROWGAGE keyboard callback to the given function.
 */
void rgSetKeyboardCallback( void (*f)(unsigned char key, int x, int y) ) {
    glutKeyboardFunc(f);
}

/*
 * Calls all the turn callbacks.
 */
void rgDoTurn() {
    int i;
    if( swap_arrays ) {
        for(i = 0; i < backarray_size; i++) {
            (backarray[i])();
        }
    }
    else {
        for(i = 0; i < turnarray_size; i++) {
            (turnarray[i])();
        }
    }
}
